#include "ufw_protocol.h"
#include <cstdint>
#include <queue>
#include <functional>
#include <random>

#include <iostream>

const uint16_t ufw_protocol::crc16_ccitt_table[] = {
    0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7, 0x8108, 0x9129, 0xa14a,
    0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef, 0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6, 0x9339, 0x8318, 0xb37b,
    0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de, 0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485, 0xa56a, 0xb54b, 0x8528,
    0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d, 0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4, 0xb75b, 0xa77a, 0x9719,
    0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc, 0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823, 0xc9cc, 0xd9ed, 0xe98e,
    0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b, 0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12, 0xdbfd, 0xcbdc, 0xfbbf,
    0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a, 0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41, 0xedae, 0xfd8f, 0xcdec,
    0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49, 0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70, 0xff9f, 0xefbe, 0xdfdd,
    0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78, 0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f, 0x1080, 0x00a1, 0x30c2,
    0x20e3, 0x5004, 0x4025, 0x7046, 0x6067, 0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e, 0x02b1, 0x1290, 0x22f3,
    0x32d2, 0x4235, 0x5214, 0x6277, 0x7256, 0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d, 0x34e2, 0x24c3, 0x14a0,
    0x0481, 0x7466, 0x6447, 0x5424, 0x4405, 0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c, 0x26d3, 0x36f2, 0x0691,
    0x16b0, 0x6657, 0x7676, 0x4615, 0x5634, 0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab, 0x5844, 0x4865, 0x7806,
    0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3, 0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a, 0x4a75, 0x5a54, 0x6a37,
    0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92, 0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9, 0x7c26, 0x6c07, 0x5c64,
    0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1, 0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8, 0x6e17, 0x7e36, 0x4e55,
    0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0
};

uint16_t ufw_protocol::crc_16(uint8_t *data, uint16_t len)
{
    uint16_t crcVal = 0x0000;
    for(uint16_t i = 0; i < len; i++) {
        crcVal = (crcVal << 8) ^ crc16_ccitt_table[((crcVal >> 8) ^ data[i]) & 0xff];  //CCITT
    }
    return crcVal;
}

void ufw_protocol::crc_sum_16(uint16_t& crc, uint8_t *data, uint16_t len)
{
    for(uint16_t i = 0; i < len; i++) {
        crc = (crc << 8) ^ crc16_ccitt_table[((crc >> 8) ^ data[i]) & 0xff];  //CCITT
    }
}

ufw_protocol::ufw_protocol()
{
    memset(_io_node_list, 0, sizeof(_io_node_list));
    memset(&_file_trans, 0, sizeof(_file_trans));

    std::default_random_engine e;
    _ptl_send_seq = e();

    std::cout << "ufw ptl creator" <<std::endl;
}

ufw_protocol::~ufw_protocol()
{
    for(int i = 0;i < PTL_LAN_NUM; i++) {
        if(_io_node_list[i].en == 0) {
            if(_io_node_list[i].read_temp.msg.payload) {
                delete [] _io_node_list[i].read_temp.msg.payload;
            }
        }
    }

    std::cout << "ufw ptl destory" <<std::endl;
}

void ufw_protocol::ptl_run(uint16_t ms)
{
    jl_ptl_run(ms);
}

int  ufw_protocol::ptl_io_in(uint8_t *data, uint16_t len)
{
    return jl_ptl_io_in(0, data, len);
}

void ufw_protocol::register_send_fun(std::function<void(const char*, uint16_t)> fun_send)
{
    _send_data = std::move(fun_send);
}

void ufw_protocol::jl_ptl_run(uint16_t ms)
{
    static uint16_t one_ms = 0;

    one_ms += ms;
    if(one_ms >= 1000) {
        one_ms = 0;

        file_trans_offline();
    }
}

int ufw_protocol::jl_ptl_io_request()
{
    for(int i = 0;i < PTL_LAN_NUM; i++) {
        if(_io_node_list[i].en == 0) {
            memset(&_io_node_list[i], 0, sizeof(jl_ptl_io_node_t));
            _io_node_list[i].en = 1;
            return i;
        }
    }

    return -1;
}

int ufw_protocol::jl_ptl_io_in(uint8_t port, uint8_t *data, uint16_t len)
{
    #define TMPBUF_LEN (16)

    static uint8_t tmp_buf[PTL_LAN_NUM][TMPBUF_LEN] = {0}, tmp_buf_flag[PTL_LAN_NUM] = {0};
    static jl_ptl_msg_t read_temp;

    int i,ret = 0;
    uint8_t C;
    jl_ptl_io_node_t *dv;
    jl_ptl_decode_msg_t *msg;

    if(port>=PTL_LAN_NUM)return __LINE__;
    if(_io_node_list[port].en == 0)return __LINE__;

    for(i = 0;i < len;i++)
    {
        C=data[i];
        dv=&_io_node_list[port];
        msg=&dv->read_temp;
        switch (dv->read_state) {
        case 0://监听包头
            if(C == JLPTL_HEAD) {
                msg->jl_ptl_data[dv->read_state] = C;
                dv->check = 0;
                dv->read_state++;
                crc_sum_16(dv->check, &C, 1);
                tmp_buf_flag[port] = 0;
            }
            break;
        case 1://监听addr
        case 2://type
        case 3://seq
        case 4://cmd bit7:0
        case 5://cmd bit15:8
        case 6://ACK
        case 7://监听长度 bit7:0
            msg->jl_ptl_data[dv->read_state] = C;
            dv->read_state++;
            crc_sum_16(dv->check, &C, 1);
            break;

        case 8: //监听长度 bit15:8
            msg->jl_ptl_data[dv->read_state] = C;
            crc_sum_16(dv->check, &C, 1);

            dv->payload_r=0;
            if(msg->msg.payload_len > JL_PTL_MSG_LEN) {
                dv->read_state = 0;
                break;
            }
            if(msg->msg.payload_len == 0) {
                dv->read_state += 2;
                break;
            }
            if(msg->msg.payload_len > TMPBUF_LEN || msg->msg.cmd < 0x8000) {
                msg->msg.payload = new uint8_t[msg->msg.payload_len];
                if(msg->msg.payload == nullptr) {
                    dv->read_state = 0;
                }
                else {
                    dv->read_state++;
                }
                tmp_buf_flag[port] = 0;
            }
            //约定,私有命令小于8个字节用全局BUF
            else {
                memset(&tmp_buf[port][0], 0, TMPBUF_LEN);
                msg->msg.payload = &tmp_buf[port][0];
                dv->read_state++;
                tmp_buf_flag[port] = 1;
            }
            break;
        case 9://监听payload
            msg->msg.payload[dv->payload_r] = C;
            dv->payload_r++;
            if(dv->payload_r >= msg->msg.payload_len){
                dv->payload_r=0;
                dv->read_state++;
            }
            crc_sum_16(dv->check, &C, 1);
            break;
        case 10://监听check bit7:0
            msg->jl_ptl_data[JLPTL_MSGLEN - 2] = C;
            dv->read_state++;
            break;
        case 11://监听check bit8:15
            msg->jl_ptl_data[JLPTL_MSGLEN - 1] = C;
            if(dv->check == msg->msg.check) {
                memcpy(&read_temp, msg, sizeof(read_temp));
                file_msg_parse(port,&read_temp);
            }
            else {
                printf("crc error: %X-%X\n", dv->check, msg->msg.check);
                ret = __LINE__;
            }
            if(tmp_buf_flag[port] == 0) {
                //数据解析完后释放内存
                delete [] msg->msg.payload;
                msg->msg.payload = nullptr;
            }

            memset(msg,0,sizeof(read_temp));
            dv->check=0;
            dv->read_state = 0;
            tmp_buf_flag[port] = 0;
            break;
        default:
            tmp_buf_flag[port] = 0;
            dv->check=0;
            dv->read_state = 0;
            break;
        }
    }

    return ret;
}

int ufw_protocol::jl_ptl_io_del(uint8_t port)
{
    if(port >= PTL_LAN_NUM)return __LINE__;
    if(_io_node_list[port].en == 0)return __LINE__;

    if(_io_node_list[port].read_temp.msg.payload) {
        delete [] _io_node_list[port].read_temp.msg.payload;
    }

    memset(&_io_node_list[port], 0, sizeof(jl_ptl_io_node_t));

    return 0;
}


void ufw_protocol::trans_file_data_push(const uint8_t* data, uint32_t data_len)
{
    if(data == nullptr || data_len == 0) return;

    while(!_file_data_fifo.empty()) _file_data_fifo.pop();

    for(uint32_t i = 0; i < data_len; i++)
        _file_data_fifo.push(data[i]);
}

uint32_t ufw_protocol::trans_file_data_pop(uint8_t* data, uint32_t data_len)
{
    uint32_t i = 0;

    for(; i < data_len; i++) {
        if(_file_data_fifo.empty()) break;

        data[i] = _file_data_fifo.front();
        _file_data_fifo.pop();
    }

    return i;
}

uint32_t ufw_protocol::file_trans_index()
{
    return _file_trans.slice_index;
}

bool ufw_protocol::is_file_trans_end()
{
    return _file_trans.slice_index >= _file_trans.slice_total;
}


void ufw_protocol::file_trans_start(uint32_t file_size, uint16_t slice_size)
{
    uint8_t buf[12] = {0};
    uint32_t slice_total = file_size / slice_size + (file_size % slice_size > 0);

    _file_trans.file_size = file_size;
    _file_trans.slice_size = slice_size;
    _file_trans.slice_total = slice_total;
    _file_trans.slice_index = 0;
    _trans_state = FILE_TRANS_REQ;

    buf[0] = 0;
    memcpy(buf + 1, &file_size, 4);
    memcpy(buf + 5, &slice_size, 2);
    memcpy(buf + 7, &slice_total, 4);

    file_data_send(CFDL_FILE_REQ, JL_ACK_SUCCESS, buf, 12);
}

int  ufw_protocol::file_trans_slice(uint32_t slice_num, uint16_t slice_size)
{
    static uint8_t trans_slice_data[2048] = {0};

    uint16_t data_len = 0;
    memcpy(trans_slice_data, &slice_num, 4);

    if(slice_num == 0) {
        data_len = trans_file_data_pop(trans_slice_data + 4, slice_size + 32);
        slice_size = data_len + 4;
    }
    else {
        data_len = trans_file_data_pop(trans_slice_data + 4, slice_size);
        slice_size = data_len + 4;
    }
    if(data_len == 0) {
        if(_file_trans.slice_index < _file_trans.slice_total) {
            printf("trans end error, slice:%d\n", _file_trans.slice_index);
            _file_trans.slice_index = _file_trans.slice_total;
        }
        return 0;
    }

    file_data_send(CFDL_FILE_SLICE, JL_ACK_SUCCESS, trans_slice_data, slice_size);
    //printf("snd silce:%d\n", slice_num);

    return 1;
}

void ufw_protocol::file_trans_end(uint8_t ret)
{
    file_data_send(CFDL_FILE_END, ret, nullptr, 0);
}

void ufw_protocol::file_msg_parse(uint8_t port,jl_ptl_msg_t *msg)
{
    (void)port;

    if(msg->cmd < CFDL_FILE_REQ || msg->cmd > CFDL_FILE_END) return;

    if(msg->type == JL_FRAME_ACK_TYPE) {
        trans_file_ack(msg);
    }
}

int ufw_protocol::file_trans_state()
{
    return _trans_state;
}

int ufw_protocol::file_trans_slice_period()
{
    int ret = 0;

    if(_file_trans.slice_index < _file_trans.slice_total) {
        uint32_t tmp = _file_trans.file_size - _file_trans.slice_index * _file_trans.slice_size;

        file_trans_slice(_file_trans.slice_index,
            tmp > _file_trans.slice_size ? _file_trans.slice_size : tmp);
        _file_trans.slice_index++;
    }
    else {
        _file_trans.slice_index++;
        printf("jl recv picture last frame\n");
        file_trans_end(JL_ACK_SUCCESS);
        ret = 1;
        _trans_state = FILE_TRANS_END;
    }

    return ret;
}

void ufw_protocol::trans_file_ack(jl_ptl_msg_t *msg)
{
    static uint8_t flag = 0;

    switch(msg->cmd) {
    case CFDL_FILE_REQ:
        _trans_state = FILE_TRANS_REQ;
        if(msg->ack == JL_ACK_SUCCESS) {
            printf("jl trans requset ack\n");
            _file_trans.slice_index = 0;
            if(file_trans_slice(_file_trans.slice_index, _file_trans.slice_size)) {
                _file_trans.slice_index++;
            }
            else {
                printf("fifo no data, current slice_num:%d\n", _file_trans.slice_index);
            }
            flag = 0;
        }
        else {
            printf("jl start recv request error: %d\n", msg->ack);
        }
        break;

    case CFDL_FILE_SLICE:
        _trans_state = FILE_TRANS_SLICE;
        if(msg->ack == JL_ACK_SUCCESS) {
            #if JL_PTL_JL_ACK_TRANS
            file_trans_slice_period();
            #endif
        }
        break;

    case CFDL_FILE_END:
        _trans_state = FILE_TRANS_END;
        printf("jl recv file end, ack:%d\n", msg->ack);
        break;

    default:
        printf("unkown msgid:%X\r\n", msg->cmd);
        break;
    }
}

void ufw_protocol::file_data_send(uint16_t cmd, uint8_t ack, uint8_t *data, uint16_t data_len)
{
    jl_ptl_msg_t file_msg = {0};

    file_msg.ack  = ack;
    file_msg.cmd = cmd;
    file_msg.type = JL_FRAME_REQ_TYPE;
    file_msg.payload_len = data_len;

    if(data_len > 0) {
        file_msg.payload = new uint8_t[data_len];
        if(file_msg.payload) {
            memcpy(file_msg.payload, data, data_len);
            jl_ptl_res_send(0, JLPTL_RES_SND_ONE, &file_msg);
            delete [] file_msg.payload;
        }
    }
    else {
        jl_ptl_res_send(0, JLPTL_RES_SND_REPEAT, &file_msg);
    }
}

void ufw_protocol::file_data_send_passthrough(uint16_t cmd, uint8_t ack, uint8_t *data, uint16_t data_len)
{

}

int ufw_protocol::jl_ptl_res_send(uint8_t port, uint8_t type, jl_ptl_msg_t *msg)
{
    if(msg->type == JL_FRAME_REQ_TYPE) {
        msg->seq = _ptl_send_seq;
        _ptl_send_seq++;
    }

    if(type == JLPTL_RES_SND_ONE) {
         jl_ptl_io_out(port, msg);
         return 0;
    }

    jl_ptl_io_out(port, msg);
    return 1;
}

int ufw_protocol::jl_ptl_io_out(uint8_t port, jl_ptl_msg_t *msg)
{
    uint16_t check;
    int sen_len = 0;

    if(port >= PTL_LAN_NUM)return __LINE__;
    if(_io_node_list[port].en == 0)return __LINE__;

    if(msg->payload_len > JL_PTL_MSG_LEN) {
        return __LINE__;
    }

    uint8_t *sen_temp = new uint8_t[sizeof(jl_ptl_msg_t) + msg->payload_len];
    if(sen_temp == nullptr){
        return __LINE__;
    }
    sen_temp[sen_len] = JLPTL_HEAD;     //头
    sen_len++;
    sen_temp[sen_len] = 0; //addr
    sen_len++;
    sen_temp[sen_len] = msg->type;
    sen_len++;
    sen_temp[sen_len] = msg->seq;
    sen_len++;
    memcpy(&sen_temp[sen_len],(const void *)&msg->cmd,2);
    sen_len += 2;
    sen_temp[sen_len] = msg->ack;
    sen_len++;
    memcpy(&sen_temp[sen_len],(const void *)&msg->payload_len,2);
    sen_len += 2;
    if(msg->payload_len > 0) {
        memcpy(&sen_temp[sen_len],msg->payload,msg->payload_len);
        sen_len += msg->payload_len;
    }

    check = crc_16(sen_temp, sen_len);
    memcpy(&sen_temp[sen_len], &check, 2);
    sen_len += 2;

    if(_send_data)
        _send_data((char *)sen_temp, sen_len);

    delete [] sen_temp;

    return 0;
}

int ufw_protocol::jl_ptl_io_out_passthrough(uint8_t port, jl_ptl_msg_t *msg)
{
    uint16_t check = 0;
    uint8_t sbuf[JLPTL_MSGLEN] = {0};
    int sen_len = 0;

    if(port >= PTL_LAN_NUM) return __LINE__;
    if(_io_node_list[port].en == 0)return __LINE__;
    if(_send_data == nullptr) return __LINE__;

    sbuf[sen_len] = JLPTL_HEAD;     //头
    sen_len++;
    sbuf[sen_len] = 0; //addr
    sen_len++;
    sbuf[sen_len] = msg->type;
    sen_len++;
    sbuf[sen_len] = msg->seq;
    sen_len++;
    memcpy(&sbuf[sen_len],(const void *)&msg->cmd,2);
    sen_len += 2;
    sbuf[sen_len] = msg->ack;
    sen_len++;
    memcpy(&sbuf[sen_len],(const void *)&msg->payload_len,2);
    sen_len += 2;
    crc_sum_16(check, sbuf, sen_len);
    if(msg->payload_len > 0) {
        crc_sum_16(check, msg->payload, msg->payload_len);
    }
    _send_data((char *)sbuf, sen_len);
    if(msg->payload_len > 0)
        _send_data((char*)msg->payload, msg->payload_len);
    _send_data((char*)&check, 2);

    return 0;
}


void ufw_protocol::file_trans_offline()
{

}

